package happy.diandiankong.api.resource;

import com.facepp.http.HttpRequests;
import com.facepp.http.PostParameters;
import com.google.common.collect.ImmutableMap;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import happy.diandiankong.api.db.FaceDao;
import happy.diandiankong.api.db.PictureDao;
import happy.diandiankong.api.db.UserDao;
import happy.diandiankong.api.model.Faces;
import happy.diandiankong.api.model.Pictures;
import happy.diandiankong.api.model.Users;
import happy.diandiankong.api.util.IdUtil;
import happy.diandiankong.api.util.StringIdUtil;
import net.coobird.thumbnailator.Thumbnails;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.json.JSONObject;
import org.skife.jdbi.v2.DBI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.servlet.annotation.MultipartConfig;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import static happy.diandiankong.api.constant.Constants.GROUP_NAME;

/**
 * Title.
 * <p>
 * Description.
 *
 * @author Bill Lv {@literal <billcc.lv@hotmail.com>}
 * @version 1.0
 * @since 2016-10-30
 */
@Path("/api/face")
@Produces(MediaType.APPLICATION_JSON)
@Api("/face")
@MultipartConfig(maxFileSize = 10485760 // 10Mb max
        , fileSizeThreshold = 524288 // 512 Kb before buffering to disk
        , maxRequestSize = 10240 // 10Kb of meta data
)
public class FaceResource {
    static final Logger LOGGER = LoggerFactory.getLogger(FaceResource.class);

    final DBI dbi;
    final String uploadPath;
    final Double compressRatio;
    final String compressTmpPath;
    final String domain;

    public FaceResource(DBI dbi, String uploadPath, Double compressRatio, String compressTmpPath, String domain) {
        this.dbi = dbi;
        this.uploadPath = uploadPath;
        this.compressRatio = compressRatio;
        this.compressTmpPath = compressTmpPath;
        this.domain = domain;
    }

    @POST
    @Path("/verify")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @ApiOperation("verify face")
    public Response veriFace(@FormDataParam("webcam") InputStream uploadedInputStream,
                             @FormDataParam("webcam") FormDataContentDisposition fileDetail) throws Exception {

        final Long picId = uploadPicture(uploadedInputStream, fileDetail);
        Pictures picture;
        try (PictureDao picturesDAO = dbi.open(PictureDao.class)) {
            picture = picturesDAO.findById(picId);
        }

        final File fileCached = Paths.get(uploadPath, picture.getStoreName()).toFile();

        HttpRequests httpRequests = new HttpRequests("79703d127720b84da26fa4690008d27a", "7Rd2wSbmj0bsBcqpwOenFJDWLkmvLt8W", true, true);

        final String uri = domain + "/api/img/download/" + picId;

        System.out.println(httpRequests.recognitionIdentify(
                new PostParameters().setGroupName(GROUP_NAME).setImg(fileCached)));


        try (FaceDao faceDao = dbi.open(FaceDao.class)) {
            final List<Faces> faces = faceDao.findAll();
            if (faces == null || faces.isEmpty()) {
                return Response.ok(ImmutableMap.of("code", -2, "message", "not faces trained")).build();
            }
            System.out.println("\nrecognition/verify");

            for (Faces face : faces) {
                final JSONObject jsonObject = httpRequests.recognitionVerify(new PostParameters().setPersonName(String.valueOf(face.getUid())).setFaceId(
                        face.getFaceId()));
                if (jsonObject.getBoolean("is_same_person")) {
                    return Response.ok(ImmutableMap.of("code", 0, "message", "success", "result", ImmutableMap.of("isYou", true))).build();
                }
            }
            return Response.ok(ImmutableMap.of("code", 0, "message", "success", "result", ImmutableMap.of("isYou", false))).build();
        }
    }

    @POST
    @Path("/train")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @ApiOperation("Upload user face img")
    public Response trainFace(@QueryParam("uid") String uid,
                              @FormDataParam("webcam") InputStream uploadedInputStream,
                              @FormDataParam("webcam") FormDataContentDisposition fileDetail) throws Exception {
        LOGGER.info("uid {}", uid);

        try (UserDao userDao = dbi.open(UserDao.class)) {
            final Users user = userDao.findById(Long.parseLong(uid));
            if (user == null) {
                return Response.ok(ImmutableMap.of("code", -1, "message", "no such uid")).build();
            }
        }

        final Long picId = uploadPicture(uploadedInputStream, fileDetail);
        Pictures picture;
        try (PictureDao picturesDAO = dbi.open(PictureDao.class)) {
            picture = picturesDAO.findById(picId);
        }

        final File fileCached = Paths.get(uploadPath, picture.getStoreName()).toFile();
        HttpRequests httpRequests = new HttpRequests("79703d127720b84da26fa4690008d27a", "7Rd2wSbmj0bsBcqpwOenFJDWLkmvLt8W", true, true);

        JSONObject result = httpRequests.detectionDetect(new PostParameters().setImg(fileCached).setFaceId(StringIdUtil.generateId()));

        System.out.println("\nperson/create");
        System.out.println(httpRequests.personCreate(new PostParameters().setPersonName(uid)));

        System.out.println("\nperson/add_face");
        for (int i = 0; i < result.getJSONArray("face").length(); ++i) {
            final String faceId = result.getJSONArray("face").getJSONObject(i).getString("face_id");
            System.out.println(httpRequests.personAddFace(new PostParameters().setPersonName(uid).setFaceId(faceId)));
            try (FaceDao faceDao = dbi.open(FaceDao.class)) {
                final Faces face = faceDao.findByUidAndFaceId(Long.parseLong(uid), faceId);
                if (face == null) {
                    faceDao.insert(IdUtil.generateId(), Long.parseLong(uid), faceId);
                }
            }
        }

        //group/add_person
        System.out.println("\ngroup/add_person");
        new PostParameters().setGroupName(GROUP_NAME).setPersonName(uid).getMultiPart().writeTo(System.out);
        System.out.println(httpRequests.groupAddPerson(new PostParameters().setGroupName(GROUP_NAME).setPersonName(uid)));

        //recognition/train
        JSONObject syncRet;

        System.out.println("\ntrain/Identify");
        syncRet = httpRequests.trainIdentify(new PostParameters().setGroupName(GROUP_NAME));
        System.out.println(syncRet);
        System.out.println(httpRequests.getSessionSync(syncRet.getString("session_id")));

        System.out.println("\ntrain/verify");
        for (int i = 0; i < result.getJSONArray("face").length(); ++i) {
            syncRet = httpRequests.trainVerify(new PostParameters().setPersonName(uid));
            System.out.println(httpRequests.getSessionSync(syncRet.get("session_id").toString()));
        }

        return Response.ok(
                ImmutableMap.of("id", picId))
                .build();
    }

    private Long uploadPicture(final InputStream inputStream, final FormDataContentDisposition fileDetail)
            throws Exception {
        final int width;
        final int height;
        File tmpFileToAdd = null;
        final String storeName;
        try {
            final String uniqueName = UUID.randomUUID().toString();
            java.nio.file.Path tmpFile = Paths.get(compressTmpPath, uniqueName);
            tmpFileToAdd = tmpFile.toFile();
            if (!tmpFileToAdd.getParentFile().exists()) {
                tmpFileToAdd.getParentFile().mkdirs();
            }
            Files.copy(inputStream, tmpFile);
            BufferedImage bufferedImage = ImageIO.read(new FileInputStream(tmpFile.toFile()));

            storeName = uniqueName + ".JPG";
            File storeFile = Paths.get(uploadPath, storeName).toFile();
            width = bufferedImage.getWidth();
            height = bufferedImage.getHeight();
            Thumbnails.of(new FileInputStream(tmpFile.toFile()))
                    .size(width, height)
                    .outputQuality(compressRatio)
                    .toFile(storeFile);
        } finally {
            if (tmpFileToAdd != null) {
                tmpFileToAdd.delete();
            }
        }

        try (final PictureDao picturesDAO = dbi.open(PictureDao.class)) {
            final long id = IdUtil.generateId();
            final String uri = domain + "/api/img/download/" + id;
            picturesDAO.insert(id, fileDetail.getFileName(), width, height, uri, storeName, new Date());
            return id;
        }

    }
}
